<?php

namespace App\Models\Plane\PlaneOrders\Repositories;

use App\Events\Plane\PlaneOrder\PlaneOrderCreatedEvent;
use App\Events\Plane\PlaneOrder\PlaneOrderPaidEvent;
use App\Events\Plane\PlaneOrder\PlaneOrderStateUpdatedEvent;
use App\Exceptions\NotRealizeInstanceException;
use App\Exceptions\TooManyActionException;
use App\Exceptions\TrainOrderDeletedException;
use App\Http\Resources\Plane\PlaneOrder\PlaneOrderResource;
use App\Jobs\Train\TrainOrder\ProcessUpdateTripCostPaySn;
use App\Models\Orders\Orders;
use App\Models\Orders\Repositories\Interfaces\OrdersRepositoryInterface;
use App\Models\Plane\PlaneOrderFlight\PlaneOrderFlight;
use App\Models\Plane\PlaneOrderFlight\Repositories\Interfaces\PlaneOrderFlightRepositoryInterface;
use App\Models\Plane\PlaneOrderPassengers\PlaneOrderPassenger;
use App\Models\Plane\PlaneOrderPassengers\Repositories\Interfaces\PlaneOrderPassengerRepositoryInterface;
use App\Models\Plane\PlaneOrders\PlaneChangeProviderExtension;
use App\Models\Plane\PlaneOrders\PlaneOrder;
use App\Models\Plane\PlaneOrders\Repositories\Interfaces\PlaneOrderRepositoryInterface;
use App\Models\Trip\OrderBusiness\Repositories\Interfaces\BusinessOrderSnMapRepositoryInterface;
use App\Models\Trip\OrderPay\OrderPay;
use App\Models\Trip\OrderPay\Repositories\Interfaces\OrderPayRepositoryInterface;
use App\Models\Trip\TripOrderCosts\Repositories\Interfaces\TripOrderCostsRepositoryInterface;
use App\Models\Trip\TripOrderCosts\Repositories\Interfaces\TripOrderPassengerCostsRepositoryInterface;
use App\Models\Trip\TripOrderCosts\TripOrderCost;
use App\Models\Trip\TripOrderCosts\TripOrderPassengerCost;
use App\Models\Trip\TripOrderRefund\RefundDetail;
use App\Models\Trip\TripOrderRefund\TripOrderRefund;
use App\Notifications\Plane\PlaneOrder\TicketFailedNotify;
use App\Notifications\Plane\PlaneOrder\TicketSuccessNotify;
use App\Trip\Enums\OrderBusiness;
use App\Trip\Enums\Plane\PlaneChangeTypes;
use App\Trip\Enums\Plane\PlaneRefundTypes;
use App\Trip\Enums\TripTypes;
use App\Trip\Orders\BookOrders\OrderChange\PlaneOrderChange;
use App\Trip\Orders\TransferFlag;
use App\Trip\Orders\TripNo;
use App\Trip\Orders\TripOrderState;
use App\Trip\Orders\TripType;
use App\Trip\Repository\BaseRepository;
use Carbon\Carbon;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Exception;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Throwable;

class PlaneOrderRepository extends BaseRepository implements PlaneOrderRepositoryInterface
{
    public function __construct(PlaneOrder $model)
    {
        parent::__construct($model);
    }

    /**
     * @param array $param
     * @param int   $page_size
     * @return Collection
     * @throws Exception
     */
    public function lists(array $param, int $page_size): Collection
    {
        $total  = $this->listCommonQuery($param)
                       ->count();
        $isSort = isset($param['sort_key']) && isset($param['sort_value']) && in_array($param['sort_value'], [1, 2]);
        /**
         * @var Paginator $listData
         */
        $listData = $this->listCommonQuery($param)
                         ->with('passengers', function ($q) {
                             return $q->select([
                                 'order_sn',
                                 'truename',
                                 'ticket_price',
                                 'order_state',
                                 'passenger_id',
                                 'sequence',
                                 'passenger_type',
                             ]);
                         })
                         ->with('flight_info', function ($q) {
                             return $q->select([
                                 'order_sn',
                                 'flight_no',
                                 'from_code',
                                 'to_code',
                                 'from_name',
                                 'from_name_uy',
                                 'to_name',
                                 'to_name_uy',
                                 'start_at',
                                 'arrive_at',
                                 'day_difference',
                                 'run_time',
                             ]);
                         })
                         ->when($isSort, function ($query) use ($param) {
                             $sort = [
                                 1 => "ASC",
                                 2 => "DESC",
                             ];
                             if ($param['sort_key'] == 'start_at') {
                                 $query->orderBy('trip_date', $sort[$param['sort_value']]);
                             }

                             if ($param['sort_key'] == 'order_at') {
                                 $query->orderBy('created_at', $sort[$param['sort_value']]);
                             }
                         })
                         ->orderByDesc('id')
                         ->simplePaginate($page_size);


        $page_size    = $page_size == 0 ? 1 : $page_size;
        $last_page    = ceil($total / $page_size);
        $current_page = $listData->currentPage();
        $orders       = $listData->items();

        return collect(compact('orders', 'total', 'last_page', 'current_page'));
    }

    /**
     * @throws Exception
     */
    private function listCommonQuery(array $param)
    {

        return $this->model
            ->where(function ($query) use ($param) {
                if (isset($param['is_valid']) && $param['is_valid'] == 1) {
                    $query->whereIn('order_state', TripOrderState::valid_states());
                }
            })
            ->when(isset($param['origin_order_sn']) && $param['origin_order_sn'], function ($query) use ($param) {
                $query->where('origin_order_sn', $param['origin_order_sn']);
            })
            ->when(isset($param['pay_state']) && $param['pay_state'], function ($query) use ($param) {
                $query->where('pay_state', $param['pay_state']);
            })
            ->when(isset($param['change_flag']) && $param['change_flag'], function ($query) use ($param) {
                $query->where('change_flag', $param['change_flag']);
            })
            ->when(isset($param['order_state']) && $param['order_state'], function ($query) use ($param) {
                if (is_array($param['order_state'])) {
                    $query->whereIn('order_state', $param['order_state']);
                } else {
                    $query->where('order_state', $param['order_state']);
                }
            })
            ->when(isset($param['provider_pay_state']) && $param['provider_pay_state'], function ($query) use ($param) {
                if (is_array($param['provider_pay_state'])) {
                    $query->whereIn('provider_pay_state', $param['provider_pay_state']);
                } else {
                    $query->where('provider_pay_state', $param['provider_pay_state']);
                }
            })
            ->when(isset($param['order_sn']) && $param['order_sn'], function ($query) use ($param) {
                if (isset($param['order_sn']['operator'])) {
                    $query->where('order_sn', $param['order_sn']['operator'], $param['order_sn']['value']);
                } else {
                    $query->where('order_sn', $param['order_sn']);
                }
            })
            ->when(isset($param['trip_id']) && $param['trip_id'], function ($query) use ($param) {
                $query->where('trip_id', $param['trip_id']);
            })
            ->when(isset($param['user_id']), function ($query) use ($param) {
                $query->where('user_id', $param['user_id']);
            })
            ->where(function ($q) use ($param) {
                // 是否忽略不显示隐藏的 主要用于 : 改签 多次,查询兄弟 改签子单
                if (!(isset($param['ignore_is_show']) && $param['ignore_is_show'])) {
                    // 默认只显示正常的
                    $q->where('is_show', 1);
                }
            })
            ->where('delete_state', TripOrderState::DELETE_STATE_NOT_DELETE);
    }

    /**
     * @param array $param
     * @param bool  $to_show
     * @return Collection
     * @throws Exception
     */
    public function listAll(array $param, bool $to_show = true): Collection
    {
        $lists = $this->listCommonQuery($param)
                      ->orderByDesc('id')
                      ->get();
        if ($to_show) {
            return collect($lists)->map(fn($order_info) => $this->setOrderShowData($order_info));
        } else {
            return $lists;
        }
    }

    /**
     * @throws Exception
     */
    private function setOrderShowData(PlaneOrder $order_info): array
    {
        $order_info['can_delete']                    = $order_info->can_delete;
        $order_info['can_cancel']                    = $order_info->can_cancel;
        $order_info['order_state_desc']              = $order_info->order_state_desc;
        $order_info['has_refund']                    = $order_info->has_refund;
        $order_info['trip_costs']                    = $order_info->trip_costs()->get();
        $order_info['passengers']                    = $order_info->getPassengersToShow();
        $order_info['can_refund']                    = collect($order_info['passengers'])->where('can_refund', 1)->count() > 0 ? 1 : 0;
        $order_info['can_change']                    = collect($order_info['passengers'])->where('can_change', 1)->count() > 0 ? 1 : 0;
        $order_info['other_fee']                     = $order_info->getOtherFees();
        $flight_info                                 = $order_info->flight_info;
        $order_info['flight_info']                   = $flight_info;
        $order_info['flight_info']['from_city_code'] = $flight_info->getFromCityCode();
        $order_info['flight_info']['to_city_code']   = $flight_info->getToCityCode();
        $order_info['max_pay_at']                    = $order_info->getMaxPayAt();
        $order_info['price_details']                 = $order_info->getPriceDetails();
        $order_info['can_refund_types']              = $order_info->refund_types();
        $order_info['can_change_types']              = $order_info->change_types();
        // check_prices 用于退改规则 价格传参
        $adultPassengers = $order_info->getAdultPassengers();
        $childPassengers = $order_info->getChildPassengers();
        $check_prices    = [];
        if ($adultPassengers) {
            $check_prices['adult_price'] = collect($adultPassengers)->first()['ticket_price'];
        }

        if ($childPassengers) {
            $check_prices['child_price'] = collect($childPassengers)->first()['ticket_price'];
        }
        $order_info['check_prices'] = $check_prices;
        return collect($order_info)->toArray();
    }

    /**
     * @param array $orders
     * @return array
     */
    public function createPlaneOrders(array $orders): array
    {
        return [];
    }

    /**
     * @param PlaneOrder $origin_order_info
     * @param array      $new_order_data
     * @return PlaneOrder
     * @throws Exception
     */
    public function changePlaneOrder(PlaneOrder $origin_order_info, array $new_order_data): PlaneOrder
    {
        $new_order_data['origin_order_sn'] = $origin_order_info->getOrderSn();
        $new_order_data['order_state']     = TripOrderState::ORDER_STATE_CHANGE_WAITING_PAY; // 改签待支付
        $new_order_data['change_flag']     = TripOrderState::ORDER_CHANGE_FLAG_NEW;
        $planeOrderChange                  = new PlaneOrderChange($origin_order_info, $new_order_data['passengers']);
        $is_ignore_worktime_error          = $new_order_data['ignore_worktime_error'] ?? '';
        if ($is_ignore_worktime_error === true) {
//            $planeOrderChange->setIgnoreWorkTimeError();
        }
        if (!$planeOrderChange->canChange()) {
            throw new Exception($planeOrderChange->getErrorMsg());
        }
        $this->update(['change_flag' => TripOrderState::ORDER_CHANGE_FLAG_ORIGIN], $origin_order_info);
        $extension = $new_order_data['extension'];
        unset($new_order_data['extension']);
        $new_change_order = $this->createPlaneOrder($new_order_data);
        // 如果 存在extension 保存
        if (!empty($extension)) {
            $ext = PlaneChangeProviderExtension::create([
                'order_sn'  => $new_change_order->getOrderSn(),
                'extension' => json_encode($extension, 64 | 256),
            ]);
        }
        return $new_change_order;
    }

    /**
     * @param array $data
     * @return PlaneOrder
     * @throws Exception
     */
    public function createPlaneOrder(array $data): PlaneOrder
    {
        $data['out_order_id']    = '';
        $round_trip_flag         = $data['round_trip_flag'] ?? 0; // 往返标识
        $data['round_trip_flag'] = in_array($round_trip_flag, [0, 1, 2]) ? $round_trip_flag : 0;
        $data['refund_types']    = PlaneRefundTypes::defaultType();             // 默认可退类型
        $data['change_types']    = PlaneChangeTypes::defaultType();             // 默认可改类型
        /**
         * @var PlaneOrder $order
         */
        $order = $this->create($data);
        /**
         * 订单航班信息
         */
        $orderFlightRepo = app(PlaneOrderFlightRepositoryInterface::class);
        $flight          = $orderFlightRepo->addFlight($order, $data['plane_info']);
        /**
         * 更新实际航班数据
         */
        $this->saveRealFlight($order, $flight);
        /**
         * 订单乘客信息
         */
        $orderPassengerRepo = app(PlaneOrderPassengerRepositoryInterface::class);
        $orderPassengerRepo->addPassengers($order, $data['passengers']);

        /**
         * 花费明细
         */
        $costsRepo = app(TripOrderCostsRepositoryInterface::class);
        $costsRepo->addMore($order, $data['price_details']);
        /**
         * 统一处理订单状态
         */
        $this->updateOrderState($order, [
            'order_state' => $order->getOrderState(),
        ]);

        /**
         * 自动取消
         */
        event(new PlaneOrderCreatedEvent($order));
        return $order;
    }

    /**
     * 保存
     * @param PlaneOrder       $order
     * @param PlaneOrderFlight $flight
     * @return bool
     */
    private function saveRealFlight(PlaneOrder $order, PlaneOrderFlight $flight): bool
    {
        return $this->update([
            'flight_no' => $flight->getFlightNo(),
            'from_code' => $flight->getFromCode(),
            'to_code'   => $flight->getToCode(),
            'trip_date' => $flight->getDepartAt(),
        ],
            $order);
    }

    /**
     * @param PlaneOrder      $current_order
     * @param array           $update_data
     * @param Collection|null $update_passengers
     * @return bool
     * @throws Exception
     */
    public function updateOrderState(PlaneOrder $current_order, array $update_data, Collection $update_passengers = null): bool
    {
        $update   = new UpdateOrderStateBusiness($current_order, $update_data, $update_passengers);
        $isUpdate = $update->updateOrderState();

        if ($isUpdate) {
            event(new PlaneOrderStateUpdatedEvent($current_order));
        }

        $current_order->refresh();
        return $isUpdate;
    }

    /**
     * @param string $out_order_sn
     * @return PlaneOrder
     */
    public function getOneOrdersByOutOrderSn(string $out_order_sn): PlaneOrder
    {
        return $this->findOneBy([
            'out_order_sn' => $out_order_sn,
        ]);
    }

    /**
     * @param Orders $order
     * @return PlaneOrder
     * @throws Exception
     */
    public function getOrderInfoByOrder(Orders $order): PlaneOrder
    {
        if (!$order->isPlaneOrder()) {
            throw new Exception("error");
        }
        return $this->getOrderInfo($order->getOrderSn());
    }

    /**
     * @param string $order_sn
     * @return PlaneOrder|null
     * @throws TrainOrderDeletedException
     */
    public function getOrderInfo(string $order_sn): ?PlaneOrder
    {
        /**
         * @var PlaneOrder $order_info
         */
        $order_info = $this->findOneBy(['order_sn' => $order_sn]);
        if ($order_info && $order_info->isDelete()) {
            throw new TrainOrderDeletedException("订单已删除", $order_info->getOriginOrderSn());
        }
        return $order_info;
    }

    /**
     * @param string $order_sn
     * @return array
     * @throws Exception
     */
    public function getOrderShowInfoDatasByOrderSn(string $order_sn): array
    {
        $order_info = $this->getOrderInfo($order_sn);
        return $order_info ? $this->getOrderShowInfoDatasByPlaneOrder($order_info) : [];
    }

    /**
     * @param PlaneOrder $order
     * @return array
     * @throws Exception
     */
    public function getOrderShowInfoDatasByPlaneOrder(PlaneOrder $order): array
    {
        $order_info          = $this->setOrderShowData($order);
        $price_details       = $order_info['price_details'];
        $origin_order_info   = null;
        $other_change_orders = [];
        if ($origin_order_sn = $order->getOriginOrderSn()) {
            $origin_order_info   = new PlaneOrderResource($this->getOrderShowInfo($origin_order_sn));
            $other_change_orders = PlaneOrderResource::collection($this->getOtherChangedOrders($order, true));
        }
        $order_info = new PlaneOrderResource($order_info);
        $order->refresh();
        return compact('order_info', 'other_change_orders', 'origin_order_info', 'price_details');

    }

    /**
     * @param string $order_sn
     * @return array
     * @throws TrainOrderDeletedException
     */
    public function getOrderShowInfo(string $order_sn): array
    {
        $order_info = $this->getOrderInfo($order_sn);
        if ($order_info) {
            $order_info = $this->setOrderShowData($order_info);
        } else {
            return [];
        }
        return $order_info;
    }

    /**
     * @param PlaneOrder $order
     * @param bool       $to_show
     * @return Collection
     */
    public function getOtherChangedOrders(PlaneOrder $order, bool $to_show): Collection
    {
        return collect();
    }

    /**
     * @param PlaneOrder $order
     * @param bool       $manual
     * @return bool
     * @throws Exception|Throwable
     */
    public function cancelOrder(PlaneOrder $order, bool $manual): bool
    {
        if ($order->isCancel()) {
            Log::error("飞机票取消订单:订单已取消,{$order->getOrderSn()}");
            throw new Exception("订单已取消");
        }

        if (!$order->isCanCancel()) {
            Log::error("飞机票取消订单:订单无法取消,{$order->getOrderSn()}");
            throw new Exception("出票结果确认中，暂无法取消，请稍后");
        }

        /**
         * 如果是新改签订单
         */
        if ($order->isChangeNew()) {
            $this->orderIsPay($order);
            return $this->cancelChangeOrder($order, $manual);
        }

        $order_state   = $this->getCancelState($manual);
        $cancel_orders = [];// 要取消的订单
        // 如果是中转订单，取消另一个订单
        if ($order->getTransferFlag() > 0) {
            $trip_orders = $this->getTripOrders($order);
            foreach ($trip_orders as $trip_order) {
                if ($trip_order->getTripType() != TripType::TRIP_TYPE_PLANE) {
                    throw new NotRealizeInstanceException("其他trip类型的退款暂未实现", 404);
                }
                $this->orderIsPay($trip_order);
                $cancel_orders[$trip_order->getOrderSn()] = $trip_order;
            }
        } else {
            $this->orderIsPay($order);
            $cancel_orders[$order->getOrderSn()] = $order;
        }
        DB::transaction(function () use ($cancel_orders, $order_state) {
            // 更新字段
            $update_data = [
                'order_state' => $order_state,
                'updated_at'  => Carbon::now()->toDateTimeString(),
            ];
            foreach ($cancel_orders as $cancel_order) {
                $this->updateOrderState($cancel_order, $update_data);
            }
        });

        return true;
    }

    private function orderIsPay(PlaneOrder $trip_order): bool
    {
        $orderPayRepo   = app(OrderPayRepositoryInterface::class);
        $pay_sn         = $trip_order->getPaysn();
        $order_pay_info = $pay_sn ? $orderPayRepo->findOneByPaySn($pay_sn) : null;
        if ($order_pay_info) {
            if ($order_pay_info->isPaySuccess()) {
                // 更新订单状态
                Log::error("飞机票取消订单失败:{$trip_order->getOrderSn()}，订单已支付");
                if (!$trip_order->isSnapUp()) {
                    throw new Exception("订单已支付,取消订单失败");
                }
                return true;
            } else if ($order_pay_info->isPayFailed() || $order_pay_info->isPayCancel()) {
                // 需要验证支付 到哪一步了
                return false;
            } else {
                // 如果是待支付 需要验证 支付结果 是否成功
                try {
                    return $order_pay_info->isPayResultDataSuccess();
                } catch (Exception $e) {
                    return false;
                }
            }
        } else {
            Log::error("飞机票取消订单，订单未支付，也未发起支付,order:{$trip_order->getOrderSn()},trip_no:{$trip_order->getTripNo()}");
        }
        return false;
    }

    /**
     * 取消改签订单
     *
     * @param PlaneOrder $order
     * @param bool       $manual 手动 还是超时自动
     *
     * @return bool
     * @throws Exception|Throwable
     */
    public function cancelChangeOrder(PlaneOrder $order, bool $manual): bool
    {
        /**
         * 改签中不能取消
         */
        if ($order->isChanging()) {
            throw new Exception("订单改签中，不能取消");
        }

        $order_state = $this->getCancelState($manual);
        DB::transaction(function () use ($order, $order_state) {
            // 更新字段
            $update_data = [
                'order_state' => $order_state,
            ];
            $this->updateOrderState($order, $update_data);
        });
        return true;
    }

    /**
     * 获取取消状态
     *
     * @param bool $manual
     *
     * @return int
     */
    private function getCancelState(bool $manual): int
    {
        return $manual ? TripOrderState::ORDER_STATE_MANUAL_CANCEL : TripOrderState::ORDER_STATE_AUTO_CANCEL;
    }

    /**
     * @param PlaneOrder $order
     * @return Collection
     */
    public function getTripOrders(PlaneOrder $order): Collection
    {
        return $this->findBy([
            'user_id'      => $order->getUserId(),
            'trip_id'      => $order->getTripId(),
            'delete_state' => 0,
        ]);
    }

    /**
     * @param PlaneOrder $order
     * @return bool
     * @throws TooManyActionException|Throwable
     */
    public function destroy(PlaneOrder $order): bool
    {
        $deleteCacheKey = "Plane:OrderDelete:{$order->getOrderSn()}";
        if (Cache::has($deleteCacheKey)) {
            throw new TooManyActionException("您操作的太快了，请稍后重试");
        }

        Cache::put($deleteCacheKey, 1, 10);
        if ($order->isDelete()) {
            return true;
        }

        $commonOrderRepo = app(OrdersRepositoryInterface::class);
        $commonOrder     = $commonOrderRepo->findByPlaneOrder($order);
        DB::transaction(function () use ($order, $commonOrderRepo, $commonOrder) {
            $updateData = [
                'delete_state' => 1,
                'deleted_at'   => Carbon::now()->toDateTimeString(),
                'delete_desc'  => "手动删除订单",
            ];
            $this->update($updateData, $order);
            $commonOrderRepo->updateOrder($commonOrder, $updateData);
        });
        Cache::forget($deleteCacheKey);
        return true;
    }

    public function updateOrder(PlaneOrder $current_order, array $update_data): bool
    {
        // TODO: Implement updateOrder() method.
    }

    /**
     * @param PlaneOrder $order
     * @param OrderPay   $orderPay
     * @return void
     * @throws Exception
     */
    public function orderPaid(PlaneOrder $order, OrderPay $orderPay)
    {
        // 已支付或 中 改签
        $new_order_state     = $order->isChangeNew() ? TripOrderState::ORDER_STATE_CHANGING : TripOrderState::ORDER_STATE_PAID;
        $current_order_state = $order->getOrderState();
        if (in_array($current_order_state, TripOrderState::canceled_order_states())) {
            throw new Exception("订单已取消,退款");
        }

        $this->updateOrderState($order, [
            'order_state' => $new_order_state,
            'pay_sn'      => $orderPay->getPaySn(),
            'pay_state'   => 1, // 已支付
        ]);
        /**
         * 推送支付成功事件
         * 如果是中转订单 先推送第一程 如果出票成功后 推送第二程
         */
        if ($order->getTripNo() == TripNo::TRIP_NO_FIRST || $order->isChangeNew()) {
            event(new PlaneOrderPaidEvent($order, $orderPay));
        }
        /**
         * 更新成本明细的 支付单号
         */
        ProcessUpdateTripCostPaySn::dispatch($order, $orderPay, 0);
    }

    /**
     * @param PlaneOrder $order
     * @param array      $data
     * @return void
     * @throws Exception|Throwable
     */
    public function ticketed(PlaneOrder $order, array $data)
    {
        if ($order->isChangeNew()) {
            $validator = Validator::make($data, [
                "passengers.*.truename"             => "required",
                "passengers.*.ticket_number"        => "required",
                "passengers.*.origin_ticket_number" => "required",
            ]);
        } else {
            $validator = Validator::make($data, [
                "passengers.*.truename"              => "required",
                "passengers.*.identity_code"         => "required",
                "passengers.*.ticket_number"         => "required",
                "passengers.*.fuel_fee"              => "required",
                "passengers.*.construct_fee"         => "required",
                "passengers.*.provider_ticket_price" => "required",
            ]);
        }

        if ($validator->fails()) {
            throw new Exception($validator->errors()->first());
        }
        // 此处分两种情况 1 是下单出票成功回调 2 是改签出票成功回调
        /**
         * @var PlaneOrderPassenger $passenger
         * @var PlaneOrderPassenger $origin_order_passenger
         */
        $orderPassengerRepo  = app(PlaneOrderPassengerRepositoryInterface::class);
        $passengers          = $order->getPassengers();
        $ticketed_passengers = $data['passengers'];
        DB::transaction(function () use (
            $order, $data, $orderPassengerRepo,
            $passengers,
            $ticketed_passengers
        ) {
            $order_state = $order->getChangeFlag() == 0 ? TripOrderState::ORDER_STATE_TICKETED : TripOrderState::ORDER_STATE_CHANGED;
            // 订单状态根据乘客数量 如果是 拆单订单
            $origin_order_sn         = $order->getOriginOrderSn();
            $origin_order_info       = $origin_order_sn ? $this->getOrderInfo($origin_order_sn) : null;
            $origin_order_passengers = $origin_order_sn ? $origin_order_info->getPassengers() : [];
            $ticket_number           = "";
            foreach ($passengers as $passenger) {
                $origin_order_passenger = $origin_order_sn ? collect($origin_order_passengers)
                    ->where('truename', $passenger->getTrueName())
                    ->firstWhere('identity_code', $passenger->getIdentityCode()) : null;
                if ($order->isChangeNew()) {
                    // 改签新单 用姓名 + 原订单票号 匹配
                    $passengerData = collect($ticketed_passengers)
                        ->where('truename', $passenger->getTrueName())
                        ->firstWhere('origin_ticket_number', $origin_order_passenger->getTicketNumber());
                } else {
                    $passengerData = collect($ticketed_passengers)
                        ->where('truename', $passenger->getTrueName())
                        ->firstWhere('identity_code', $passenger->getIdentityCode());
                }
                if (!$passengerData) {
                    throw new Exception("乘客数据异常");
                }

                if (isset($passengerData["provider_ticket_price"]) && $passengerData["provider_ticket_price"] > $passenger->getTicketPrice()) {
                    $error_msg = "票价异常：{$order->getOrderSn()},";
                    $error_msg .= "ticket:{$passengerData["ticket_number"]},";
                    $error_msg .= "订票价：{$passenger->getTicketPrice()}";
                    $error_msg .= "出票票价：{$passengerData["provider_ticket_price"]}";
                    Log::error($error_msg);
                }

                $ticket_number         = $passengerData["ticket_number"];
                $update_passenger_data = [

                    "ticket_number" => $ticket_number,
                    "order_state"   => $order_state,
                ];
                if (isset($passengerData["fuel_fee"])) {
                    $update_passenger_data['fuel_fee'] = $passengerData["fuel_fee"];
                }

                if (isset($passengerData["construct_fee"])) {
                    $update_passenger_data['construct_fee'] = $passengerData["construct_fee"];
                }

                if (isset($passengerData["provider_ticket_price"])) {
                    $update_passenger_data['provider_ticket_price'] = $passengerData["provider_ticket_price"];
                }
                if (isset($passengerData["provider_change_paid_price"])) {
                    $update_passenger_data['provider_change_paid_price'] = $passengerData["provider_change_paid_price"];
                }
                if (isset($passengerData["provider_change_fee"])) {
                    $update_passenger_data['provider_change_fee'] = $passengerData["provider_change_fee"];
                }
                if (isset($passengerData["provider_upgrade_fee"])) {
                    $update_passenger_data['provider_upgrade_fee'] = $passengerData["provider_upgrade_fee"];
                }

                /**
                 * 更新原票票价
                 */
                if ($origin_order_passenger && !$passenger->getOriginTicketPrice()) {
                    $update_passenger_data['origin_ticket_price'] = $origin_order_passenger->getTicketPrice();
                }
                $orderPassengerRepo->updatePassenger($passenger, $update_passenger_data);
            }

            // 如果乘客数量不一致的情况下 订单状态只能是部分 改签成功
            if (0 != $order->getChangeFlag() && count($ticketed_passengers) != $passengers->count()) {
                $order_state = TripOrderState::ORDER_STATE_CHANGED_PART;
            }

            $this->updateOrderState($order, [
                'order_state' => $order_state,
            ]);

        });


        if ($order->isChangeNew()) {
            // 改签成功通知
        } else {
            // 出票成功通知
            $order->notify((new TicketSuccessNotify($order)));
        }

    }

    /**
     * @param string $user_id
     * @return PlaneOrder|null
     */
    public function lastOrder(string $user_id): ?PlaneOrder
    {
        return $this->model->where(['user_id' => $user_id])
                           ->orderByDesc($this->model->getKeyName())
                           ->first();
    }

    /**
     * @param array $data
     * @return PlaneOrder|null
     */
    public function findOneByPlaneData(array $data): ?PlaneOrder
    {
        return $this->model
            ->where('user_id', $data['user_id'])
            ->where('from_code', $data['from_code'])
            ->where('to_code', $data['to_code'])
            ->where('trip_date', $data['trip_date'])
            ->with('passengers')
            ->first();
    }

    /**
     * @param array $data
     * @return Collection
     */
    public function checkSameTripPlaneData(array $data): Collection
    {
        // 全局验证
        return $this->model
            ->where('from_code', $data['from_code'])
            ->where('to_code', $data['to_code'])
            ->where('trip_date', Carbon::parse($data['trip_date'])->format('Y-m-d H:i'))
            ->where('flight_no', $data['flight_no'])
            ->when(isset($data['user_id']), function ($q) use ($data) {
                $q->where('user_id', $data['user_id']);
            })
            ->when(isset($data['order_sn']), function ($q) use ($data) {
                $q->where('order_sn', $data['order_sn']);
            })
            ->where('delete_state', 0)
            ->whereIn('order_state', [
                TripOrderState::ORDER_STATE_WAITING_PAY,
                TripOrderState::ORDER_STATE_CHANGE_WAITING_PAY,
                TripOrderState::ORDER_STATE_PAID,
                TripOrderState::ORDER_STATE_TICKETED,
                TripOrderState::ORDER_STATE_CHANGED,
                TripOrderState::ORDER_STATE_CHANGING,
                TripOrderState::ORDER_STATE_REFUNDING,
            ])
            ->with('passengers')
            ->get();
    }

    /**
     * @param PlaneOrder $order
     * @param string     $out_order_id
     * @param array      $subOrders 下单时服务商拆单返回的拆单数据,或者多乘客 部分改签时返回的子单数据
     * @return bool
     */
    public function saveOutOrderId(PlaneOrder $order, string $out_order_id, array $subOrders = []): bool
    {
        $businessMap = app(BusinessOrderSnMapRepositoryInterface::class);
        // 关系映射的外部订单和订单添加
        $businessMap->addOneMapByPlaneOrder($order, $out_order_id);
        $update_data = [
            'out_order_id' => $out_order_id,
        ];
        // 更新外部 订单单号,如果是抢票 更新抢票状态
        $this->update($update_data, $order);
        $this->savePassengersSubOutOrderId($order->refresh(), $subOrders);
        return true;
    }

    /**
     * @param PlaneOrder $order
     * @param array      $subOrders 后面换成 interface
     * @return void
     */
    private function savePassengersSubOutOrderId(PlaneOrder $order, array $subOrders): void
    {
        $passengerRepo = app(PlaneOrderPassengerRepositoryInterface::class);
        $passengers    = $order->getPassengers();
        // 如果没有子单号子单号就和out_order_id保持一致
        if (empty($subOrders)) {
            foreach ($passengers as $passenger) {
                $passengerRepo->updatePassenger($passenger, [
                    'sub_out_order_id' => $order->getOutOrderId(),
                ]);
            }
            return;
        }


        // sub_out_order_id 更新到乘客数据中
        $isSplit = 1;
        foreach ($subOrders as $sub_order) {
            $sub_out_order_id = $sub_order['sub_out_order_id'] ?: $order->getOutOrderId();
            foreach ($sub_order['passengers'] as $sub_order_passenger) {
                // 目前身份证号 不能重复~~
                $passenger  = $passengers->where('identity_code', $sub_order_passenger['identity_code'])->first();
                $updateData = [
                    'sub_out_order_id' => $sub_out_order_id,
                ];

                if (isset($sub_order_passenger['out_change_id'])) {
                    $updateData['out_change_id'] = $sub_order_passenger['out_change_id'];
                }
                if (isset($sub_order_passenger['id'])) {
                    $updateData['provider_passenger_id'] = $sub_order_passenger['id'];
                }
                $passengerRepo->updatePassenger($passenger, $updateData);
            }
        }
        // 标记服务商拆单
        if ($order->isChangeNew()) {
            // 如果是改签新单 判断是否乘客中的子外部单号不一样
            $change_sub_out_order_ids = $order->passengers->pluck('sub_out_order_id')->unique()->toArray();
            $isSplit                  = count($change_sub_out_order_ids) > 1 ? 1 : 0;
        }
        $this->update([
            'out_order_split' => $isSplit,
        ], $order);

    }

    /**
     * @param PlaneOrder $order
     * @param string     $pay_sn
     * @return bool
     */
    public function saveOrderPaySn(PlaneOrder $order, string $pay_sn): bool
    {
        return $this->update([
            'pay_sn' => $pay_sn,
        ], $order);
    }

    /**
     * @param PlaneOrder $order
     * @return array
     */
    public function getChangeOriginOrderNeedRefund(PlaneOrder $order): array
    {
        return [];
    }

    /**
     * @param PlaneOrder $first_order
     * @return PlaneOrder|null
     */
    public function getTripSecondOrder(PlaneOrder $first_order): ?PlaneOrder
    {
        $isTripOne    = $first_order->getTripNo() == TripNo::TRIP_NO_FIRST;
        $isTransFirst = $first_order->getTransferFlag() == TransferFlag::ORDER_TRANSFER_FLAG_FIRST;
        if (!$isTripOne || !$isTransFirst) {
            return null;
        }
        $second_trip_no = $first_order->getTripNo() + 1;
        return $this->findOneBy([
            'user_id'       => $first_order->getUserId(),
            'trip_id'       => $first_order->getTripId(),
            'transfer_flag' => TransferFlag::ORDER_TRANSFER_FLAG_SECOND,
            'trip_no'       => $second_trip_no,
            'delete_state'  => 0,
        ]);
    }

    /**
     * @param PlaneOrder $order
     * @param null       $passengers 变更订单需要乘客
     * @return void
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws Throwable
     */
    public function ticketFailedAndRefund(PlaneOrder $order, $passengers = [])
    {
        /**
         * @var TripOrderRefund $refund_info
         * @var PlaneOrder      $order_info
         */
        $costRepo   = app(TripOrderCostsRepositoryInterface::class);
        $build_data = function (PlaneOrder $order_info, $out_refund_no) use ($costRepo) {
            $refund_sn      = generate_order_id(TripTypes::PLANE, OrderBusiness::REFUND);
            $refund_details = [];
            $order_costs    = $costRepo->listByOrder($order_info);
            $order_pay_info = $order_info->order_pay_info();
            $refund_data    = [
                "user_id"         => $order_info->getUserId(),
                "refund_sn"       => $refund_sn,
                "out_refund_no"   => $out_refund_no,
                "origin_order_sn" => $order_info->getOriginOrderSn(),
                "order_sn"        => $order_info->getOrderSn(),
                "out_trade_no"    => $order_info->getPaySn(),
                "ticket_price"    => 0,
                "refund_amount"   => $order_info->getReceivable(),
                "pay_amount"      => $order_pay_info->getPayAmount(),
                "has_ticket"      => TripOrderRefund::REFUND_NO_TICKET,
                "refund_state"    => TripOrderRefund::REFUND_STATE_REFUNDING, // 出票失败需要立即退款
                "title"           => "无票退款",
                "refund_type"     => TripOrderRefund::REFUND_TYPE_NO_TICKET,
                "trip_type"       => $order_info->getTripType(),
            ];
            /**
             * @var TripOrderCost $order_cost
             */
            foreach ($order_costs as $order_cost) {
                $refund_detail_type = RefundDetail::getCostTypeToRefundDetailType($order_cost->getType());
                $refund_details[]   = [
                    "user_id"         => $order_info->getUserId(),
                    "refund_sn"       => $refund_sn,
                    "order_sn"        => $order_info->getOrderSn(),
                    "out_trade_no"    => $order_cost->getPaySn(),
                    "origin_order_sn" => $order_info->getOriginOrderSn(),
                    "type"            => $refund_detail_type,
                    "name"            => RefundDetail::getTypeDesc($refund_detail_type),
                    "amount"          => $order_cost->getPrice(),
                    "count"           => $order_cost->getCount(),
                    "trip_type"       => $order_info->getTripType(),
                ];
            }
            $refund_data['details'] = $refund_details;
            return $refund_data;
        };

        $passenger_name = $order->passengers()->first()?->getTrueName() ?: "";
        $refund_reason  = ticket_failed_refund_title($passenger_name);
        // 1 普通订单
        if ($order->isTicketing()) {
            $orders        = $this->getOrdersByOutOrderSn($order->getOutOrderSn());
            $refund        = new TripOrderRefund();
            $out_refund_no = generate_order_id(TripTypes::PLANE, OrderBusiness::REFUND);
            foreach ($orders as $order_info) {
                if ($order_info->isTicketing()) {
                    $refund_data[$order_info->getOrderSn()] = $build_data($order_info, $out_refund_no);
                }
            }

            if (empty($refund_data)) {
                return;
            }
            $refunds = [];
            DB::transaction(function () use (&$refunds, $orders, $refund, $refund_data) {
                foreach ($orders as $order_info) {
                    $this->updateOrderState($order_info, [
                        'order_state' => TripOrderState::ORDER_STATE_TICKET_FAILED,
                    ]);
                }
                $refunds = $refund->applyNoTicketRefund($refund_data);
            });
            $order->notify((new TicketFailedNotify($order)));
            $refund->refundsNow($refunds, $refund_reason);
        }

        // 2 改签订单
        if ($order->isChangeNew() && $order->isChanging()) {
            if (!$passengers) {
                // 由于可能拆单,所以需要指定关联的乘客数据
                throw new Exception("关联乘客为空");
            }

            // 验证 如果 乘客 全失败了 直接退款
            if (count($passengers) == $order->passengers()->count()) {
                $refund                            = new TripOrderRefund();
                $out_refund_no                     = generate_order_id(TripTypes::PLANE, OrderBusiness::REFUND);
                $refund_data[$order->getOrderSn()] = $build_data($order, $out_refund_no);
                DB::transaction(function () use (&$refunds, $order, $refund, $refund_data, $passengers) {
                    $this->orderChangeFailed($order, $passengers);
                    $refunds = $refund->applyNoTicketRefund($refund_data);
                });

                $refund_info = $refunds[$refund_data[$order->getOrderSn()]['refund_sn']];
                $refund_info?->refundNow($refund_reason);
            } else {
                // 验证
                foreach ($order->passengers as $order_passenger) {
                    foreach ($passengers as $data_passenger) {
                        // TODO
                    }
                }
            }
        }
    }

    /**
     * @param string $out_order_sn
     * @return Collection
     */
    public function getOrdersByOutOrderSn(string $out_order_sn): Collection
    {
        return $this->findBy([
            'out_order_sn' => $out_order_sn,
        ]);
    }

    /**
     * @param PlaneOrder  $order
     * @param             $passengers
     * @return void
     * @throws Exception
     */
    public function orderChangeFailed(PlaneOrder $order, $passengers): void
    {
        if (!$order->isChangeNew()) {
            throw new Exception("改签标识错误");
        }

        $current_order_state = $order->getOrderState();
        switch ($current_order_state) {
            case TripOrderState::ORDER_STATE_CHANGING:
            case TripOrderState::ORDER_STATE_CHANGE_WAITING_PAY:
                break;
            default:
                throw new Exception("当前状态不符合");
        }
        /**
         * 更新订单状态为改签失败~
         */
        $this->updateOrderState($order, [
            'order_state' => TripOrderState::ORDER_STATE_CHANGE_FAILED,
            'passengers'  => $passengers,
        ]);

//        $order->notify(new ChangeFailedNotify($order));
    }

    /**
     * @param PlaneOrder  $order_info
     * @param             $passengers
     * @return bool
     * @throws Exception
     */
    public function passengerApplyRefund(PlaneOrder $order_info, $passengers)
    {
        return $this->updateOrderState($order_info, [
            'order_state' => TripOrderState::ORDER_STATE_REFUNDING,
        ], collect($passengers));
    }

    /**
     * @param string $out_order_id
     * @return PlaneOrder
     */
    public function getOneOrdersByOutOrderId(string $out_order_id): PlaneOrder
    {
        return $this->findOneBy([
            'out_order_id' => $out_order_id,
        ]);
    }

    /**
     * 出票中的订单
     * @param array $condition
     * @return Collection
     * @throws Exception
     */
    public function getTicketingOrders(array $condition = []): Collection
    {
        $condition['provider_pay_state'] = 1;// 供应商已支付
        $condition['pay_state']          = 1;// 已支付
        $condition['order_state']        = TripOrderState::ORDER_STATE_PAID;
        return $this->listCommonQuery($condition)->get();
    }

    /**
     * 更新供应商的支付状态
     * @param PlaneOrder $current_order
     * @param int        $pay_state
     * @param string     $desc
     * @return bool
     */
    public function updateProviderPayState(PlaneOrder $current_order, int $pay_state, string $desc): bool
    {
        $update_data = [
            'provider_pay_state'      => $pay_state,
            'provider_pay_state_desc' => Str::limit($desc, 120),
            'provider_pay_at'         => Carbon::now()->toDateTimeString(),
        ];
        // 更新供应商支付成功的时间
        if (1 == $pay_state && $desc == '') {
            $update_data['provider_pay_at'] = Carbon::now()->toDateTimeString();
        }
        return $this->update($update_data, $current_order);
    }

    /**
     * @param array $condition
     * @return Collection
     * @throws Exception
     */
    public function getChangingOrders(array $condition = []): Collection
    {
        $condition['provider_pay_state'] = [0, 1, 5];// 供应商已支付 或待支付 可能存在不需要支付的情况
        $condition['pay_state']          = 1;        // 已支付
        $condition['order_state']        = TripOrderState::ORDER_STATE_CHANGING;
        $condition['change_flag']        = TripOrderState::ORDER_CHANGE_FLAG_NEW;
        // 如果供应商价格待确认 忽略支付状态
        if (isset($condition['provider_price_state']) && $condition['provider_price_state'] === 0) {
            $condition['pay_state']   = 0;
            $condition['order_state'] = TripOrderState::ORDER_STATE_CHANGE_WAITING_PAY;
        }
        return $this->listCommonQuery($condition)->get();
    }

    /**
     * @param array $condition
     * @return Collection
     * @throws Exception
     */
    public function getReturningOrders(array $condition = []): Collection
    {
        $condition['ignore_is_show']     = 1;        // 已支付
        $condition['provider_pay_state'] = 1;        // 已支付
        $condition['pay_state']          = 1;        // 已支付
        $condition['order_state']        = TripOrderState::ORDER_STATE_REFUNDING;
        return $this->listCommonQuery($condition)->get();
    }

    /**
     * @throws Exception|Throwable
     */
    public function refillChangingConfirmPrice(PlaneOrder $order, array $data): PlaneOrder
    {
        if (!$order->isChangeNew()) {
            throw new Exception("订单非改签订单");
        }


        $current_order_state = $order->fresh()->getOrderState();
        if ($current_order_state != TripOrderState::ORDER_STATE_CHANGE_WAITING_PAY) {
            throw new Exception("订单状态已变更");
        }
        $validator = Validator::make($data, [
            'sub_out_order_id' => 'required',
            'change_fee'       => 'required',
            'upgrade_fee'      => 'required',
        ]);


        if ($validator->fails()) {
            throw new Exception($validator->errors()->first());
        }


        // 改签订单乘客,如果存在 子单号不一样 也是价格待确认的情况
        $hasAnotherSuborderPriceCheckingPassenger = false;
        $passengers                               = $order->getPassengers();
        /**
         * @var PlaneOrderPassenger $passenger
         */
        $need_update_passengers = [];
        foreach ($passengers as $passenger) {
            if ($passenger->getOriginSubOutOrderId() != $data['sub_out_order_id']) {
                if (0 == $passenger->provider_price_state) {
                    // 存在另一个批次价格待确认的乘客
                    $hasAnotherSuborderPriceCheckingPassenger = true;
                }
            } else {
                if (0 == $passenger->provider_price_state) {
                    $need_update_passengers[] = $passenger;
                }
            }
        }

        if (empty($need_update_passengers)) {
            throw new Exception("没有价格待回填的订单乘客");
        }
        $passengerRepo     = app(PlaneOrderPassengerRepositoryInterface::class);
        $costRepo          = app(TripOrderCostsRepositoryInterface::class);
        $passengerCostRepo = app(TripOrderPassengerCostsRepositoryInterface::class);
        // 如果存在其他价格待确认的批次 订单金额无法计算,如果不存在就回填订单金额
        if (!$hasAnotherSuborderPriceCheckingPassenger) {
            DB::transaction(function () use ($order, $data, $need_update_passengers, $current_order_state, $passengerRepo, $costRepo, $passengerCostRepo) {
                $passenger_count = count($need_update_passengers);
                $total_price     = $data['change_fee'] + $data['upgrade_fee'];
                $per_total       = round($total_price / $passenger_count, 2);
                $per_change_fee  = round($data['change_fee'] / $passenger_count, 2);
                $per_upgrade_fee = round($data['upgrade_fee'] / $passenger_count, 2);

                // 订单状态不变
                $this->updateOrderState($order, [
                    'order_state'          => $current_order_state, // 价格已确认
                    'provider_price_state' => 1, // 价格已确认
                    'order_amount'         => $total_price,
                    'receivable'           => $total_price,
                ]);
                // 更新乘客数据 更新乘客资金明细
                $costs = $costRepo->listByOrder($order);
                /**
                 * @var TripOrderCost $cost
                 */
                foreach ($costs as $cost) {
                    if ($cost->getPrice() != '-1') {
                        continue;
                    }
                    if ($cost->getType() == TripOrderCost::COST_TYPE_CHANGE_SERVICE_FEE) {
                        $costRepo->updateOne($cost, [
                            'price'         => $total_price,
                            'confirm_state' => 1,
                        ]);
                    }

                }
                foreach ($need_update_passengers as $needUpdatePassenger) {
                    $passengerRepo->updatePassenger($needUpdatePassenger, [
                        'provider_price_state'       => 1,// 价格确认
                        'provider_change_pay_status' => PlaneOrderPassenger::PROVIDER_CHANGE_PAY_STATUS_WAITING_PAY,// 待支付
                        'upgrade_fee'                => $per_upgrade_fee,
                        'change_fee'                 => $per_change_fee,
                        'total_price'                => $per_total,
                        'updated_at'                 => Carbon::now()->toDateTimeString(),
                    ]);
                    $passenger_costs = $passengerCostRepo->listByOrderPassengers($needUpdatePassenger);
                    /**
                     * @var TripOrderPassengerCost $passengerCost
                     */
                    foreach ($passenger_costs as $passengerCost) {
                        if ($passengerCost->getPrice() != -1 && $passengerCost->getConfirmState() != 0) {
                            continue;
                        }
                        switch ($passengerCost->getType()) {
                            case TripOrderCost::COST_TYPE_PLANE_CHANGE_UPGRADE_FEE:
                                $passengerCostRepo->updateOne($passengerCost, [
                                    'confirm_state' => 1,
                                    'price'         => $per_upgrade_fee,
                                ]);
                                break;
                            case TripOrderCost::COST_TYPE_CHANGE_SERVICE_FEE:
                                $passengerCostRepo->updateOne($passengerCost, [
                                    'confirm_state' => 1,
                                    'price'         => $per_change_fee,
                                ]);
                                break;
                            default:
                        }
                    }
                }
            });
        }
        return $order;
    }
}